<!DOCTYPE html>
<meta charset=utf-8>
<title>Test basic functionality of scroll linked animation.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<script src="testcommon.js"></script>
<style>
  .scroller {
    overflow: auto;
    height: 100px;
    width: 100px;
    will-change: transform;
  }
  .contents {
    height: 1000px;
    width: 100%;
  }
</style>
<div id="log"></div>
<script>
  'use strict';
  promise_test(async t => {
    const animation = createScrollLinkedAnimation(t);
    const scroller = animation.timeline.scrollSource;
    const maxScroll = scroller.scrollHeight - scroller.clientHeight;

    // Wait for new animation frame  which allows the timeline to compute new
    // current time.
    await waitForNextFrame();

    // Verify initial start and current times in Idle state.
    assert_equals(animation.currentTime, null,
      "The current time is null in Idle state.");
    assert_equals(animation.startTime, null,
      "The start time is null in Idle state.");
    animation.play();
    assert_true(animation.pending, "Animation is in pending state.");
    // Verify initial start and current times in Pending state.
    assert_equals(animation.currentTime, 0,
      "The current time is zero in Pending state.");
    assert_equals(animation.startTime, 0,
      "The start time is zero in Pending state.");

    await animation.ready;
    // Verify initial start and current times in Playing state.
    assert_equals(animation.currentTime, 0,
      "The current time is zero in Playing state.");
    assert_equals(animation.startTime, 0,
      "The start time is zero in Playing state.");

    // Now do some scrolling and make sure that the Animation current time is
    // correct.
    scroller.scrollTop = 0.2 * maxScroll;
    // Wait for new animation frame  which allows the timeline to compute new
    // current time.
    await waitForNextFrame();
    assert_equals(animation.currentTime, animation.timeline.currentTime,
      "The current time corresponds to the scroll position of the scroller.");
    assert_times_equal(
      animation.effect.getComputedTiming().localTime,
      animation.timeline.currentTime,
      'Effect local time corresponds to the scroll position of the scroller.');
}, 'Animation start and current times are correct for each animation state.');

promise_test(async t => {
    const animation = createScrollLinkedAnimation(t);
    const scroller = animation.timeline.scrollSource;
    const maxScroll = scroller.scrollHeight - scroller.clientHeight;

    // Advance the scroller.
    scroller.scrollTop = 0.2 * maxScroll;
    // Wait for new animation frame  which allows the timeline to compute new
    // current time.
    await waitForNextFrame();

    // Verify initial start and current times in Idle state.
    assert_equals(animation.currentTime, null,
      "The current time is null in Idle state.");
    assert_equals(animation.startTime, null,
      "The start time is null in Idle state.");
    animation.play();
    // Verify initial start and current times in Pending state.
    assert_equals(animation.currentTime, animation.timeline.currentTime,
      "The current time is a hold time in Pending state.");
    assert_equals(animation.startTime, 0,
      "The start time is zero in Pending state.");

    await animation.ready;
    // Verify initial start and current times in Playing state.
    assert_equals(animation.currentTime, animation.timeline.currentTime,
      "The current corresponds to the scroll position of the scroller.");
    assert_equals(animation.startTime, 0,
      "The start time is zero in Playing state.");
}, 'Animation start and current times are correct for each animation state' +
    ' when the animation starts playing with advanced scroller.');

promise_test(async t => {
    const timeline = createScrollTimeline(t);
    const animation1 = createScrollLinkedAnimation(t, timeline);
    const animation2 = createScrollLinkedAnimation(t, timeline);
    const scroller = timeline.scrollSource;
    const maxScroll = scroller.scrollHeight - scroller.clientHeight;

    // Advance the scroller.
    scroller.scrollTop = 0.2 * maxScroll;
    // Wait for new animation frame  which allows the timeline to compute new
    // current time.
    await waitForNextFrame();

    // Verify initial start and current times in Idle state.
    assert_equals(animation1.currentTime, null,
      "The current time is null in Idle state.");
    assert_equals(animation1.startTime, null,
      "The start time is null in Idle state.");
    assert_equals(animation2.currentTime, null,
      "The current time is null in Idle state.");
    assert_equals(animation2.startTime, null,
      "The start time is null in Idle state.");
    animation1.play();
    animation2.play();
    // Verify initial start and current times in Pending state.
    assert_equals(animation1.currentTime, timeline.currentTime,
      "The current time corresponds to the scroll position of the scroller" +
        " in Pending state.");
    assert_equals(animation1.startTime, 0,
      "The start time is zero in Pending state.");
    assert_equals(animation2.currentTime, timeline.currentTime,
      "The current time corresponds to the scroll position of the scroller" +
        " in Pending state.");
    assert_equals(animation2.startTime, 0,
      "The start time is zero in Pending state.");

    await animation1.ready;
    await animation2.ready;
    // Verify initial start and current times in Playing state.
    assert_equals(animation1.currentTime, timeline.currentTime,
      "The current corresponds to the scroll position of the scroller.");
    assert_equals(animation1.startTime, 0,
      "The start time is zero in Playing state.");
    assert_equals(animation2.currentTime, timeline.currentTime,
      "The current corresponds to the scroll position of the scroller.");
    assert_equals(animation2.startTime, 0,
      "The start time is zero in Playing state.");
}, 'Animation start and current times are correct when multiple animations' +
    ' are attached to the same timeline.');

promise_test(async t => {
    const animation = createScrollLinkedAnimation(t);
    const scroller = animation.timeline.scrollSource;
    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    const timeRange = animation.timeline.timeRange;

    animation.play();
    await animation.ready;

    // Advance the scroller to max position.
    scroller.scrollTop =  maxScroll;
    await animation.finished;

    // Scroll back.
    scroller.scrollTop = 0.2 * maxScroll;

    // Wait for new animation frame  which allows the timeline to compute new
    // current time.
    await waitForNextFrame();
    // Verify animation state and current time on reverse scrolling.
    assert_equals(animation.playState,  'running',
      'Animation state is playing on reverse scrolling.');
    assert_equals(animation.currentTime,  0.2 * timeRange,
      'Animation current time is updated on reverse scrolling.');
}, 'Finished animation plays on reverse scrolling.');

promise_test(async t => {
    const animation = createScrollLinkedAnimation(t);
    const scroller = animation.timeline.scrollSource;
    const maxScroll = scroller.scrollHeight - scroller.clientHeight;

    animation.play();
    await animation.ready;

    // Advance the scroller to max position.
    scroller.scrollTop =  maxScroll;
    await animation.finished;

    var sent_finish_event = false;
    animation.onfinish = function() {
      sent_finish_event = true;
    };

    // Scroll back.
    scroller.scrollTop = 0.2 * maxScroll;
    // Wait for new animation frame  which allows the timeline to compute new
    // current time.
    await waitForNextFrame();
    assert_false(sent_finish_event,
                 "No animation finished event is sent on reverse scroll.");

    scroller.scrollTop = maxScroll;
    await animation.finished;

    // Wait for next frame to allow the animation to send finish events. The
    // finished promise fires before events are sent.
    await waitForNextFrame();

    assert_true(sent_finish_event,
                 "Animation finished event is sent on reaching max scroll.");
}, 'Sending animation finished events by finished animation on reverse ' +
   'scrolling.');
</script>